﻿/*
 * Vytvořeno aplikací SharpDevelop.
 * Uživatel: lebeda
 * Datum: 29.5.2012
 * Čas: 7:58
 *
 * Tento template můžete změnit pomocí Nástroje | Možnosti | Psaní kódu | Upravit standardní hlavičky souborů.
 */
// TODO - upravit hlavičky souborů
using System;
using System.Text;
using System.IO;

using Lucene.Net.Analysis;
using Lucene.Net.Analysis.Standard;
using Lucene.Net.Documents;
using Lucene.Net.Index;
using Lucene.Net.QueryParsers;
using Lucene.Net.Search;
using Lucene.Net.Store;

using System.Text.RegularExpressions;

public class LocateCommons
{

    public const string COL_TYPE = "type";
    public const string COL_CONTENT = "name";
    public const string COL_KEY = "path";
    public const string COL_TITLE = "title";
    
    public const string FS_SEPARATOR = "\\";
    
    public const int MAX_WAIT = 30; // maximum waiting cycles
    public const int SLEEP_CYCLE = 60000; // 1 minute in ms
    
    public const Lucene.Net.Util.Version LUCENE_VERSION = Lucene.Net.Util.Version.LUCENE_30;
    
    
    public const string TYPE_FILE = "Default";
    
    public const string ARCHIVE_SUFFIX = @"zip|rar|7z|tar.gz|tar.bz2|tbz|tgz|jar";
    
    public static string indexDirName()
    {
        String personalFolder = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
        return personalFolder + FS_SEPARATOR + "locate.NET";
        // TODO - output on verbose
    }

    public static void waitForReleaseIndexLock()
    {
        int cntWait = 0;
        string indexDirPath = LocateCommons.indexDirName();
        if (System.IO.Directory.Exists(indexDirPath)) {
            string lockPath = indexDirPath + FS_SEPARATOR + "write.lock";
            while (File.Exists(lockPath) && (cntWait < MAX_WAIT)) {
                cntWait++;
                LogToConsole("wait for release index lock: " + cntWait + ". min");
                System.Threading.Thread.Sleep(SLEEP_CYCLE);
            }
        }
    }


    public static Lucene.Net.Store.Directory GetIndexDirectory()
    {        
        string indexDir = LocateCommons.indexDirName();
        DirectoryInfo di = new DirectoryInfo(indexDir);
        Lucene.Net.Store.Directory directory = FSDirectory.Open(di);
        return directory;
    }

    public static void LogToConsole(string message)
    {
        DateTime dateTime = DateTime.Now;
        Console.WriteLine(dateTime.ToLocalTime() + " ... " +message);
    }

    public static bool forceCreateIndex()
    {
        bool forceCreate = !System.IO.Directory.Exists(indexDirName());
        return forceCreate;
    }

    /// <summary>
    /// Get result of search exact phrase in key column LocateCommons.COL_KEY.
    /// </summary>
    /// <param name="directory">Lucene database</param>
    /// <param name="keyVal">key value</param>
    /// <returns>Score for search, if hits.Length greater then 0, key vas succesfuly found</returns>
    public static ScoreDoc[] getSearchResultExactByKey(Lucene.Net.Store.Directory directory, string keyVal)
    {
        IndexSearcher searcher = LocateCommons.getIndexSearcher(directory);
        Analyzer simpleAnalyzer = new SimpleAnalyzer();
        Query q = new TermQuery( new Term( LocateCommons.COL_KEY, keyVal ) );
        TopScoreDocCollector collector = TopScoreDocCollector.Create(1, true);
        searcher.Search(q, collector);
        ScoreDoc[] hits = collector.TopDocs().ScoreDocs;
        return hits;
    }

    /// <summary>
    /// Check existence of exact key in Lucene database, in column LocateCommons.COL_KEY
    /// </summary>
    /// <param name="directory">Lucene database</param>
    /// <param name="keyVal">key value</param>
    /// <returns>true if exists, false if non exists</returns>
    public static bool keyExistsInDb(Lucene.Net.Store.Directory directory, string keyVal)
    {
        return getSearchResultExactByKey(directory, keyVal).Length > 0;
    }

    public static ScoreDoc[] getSearchResult(string queryStr, string queryStrPath, ref IndexSearcher searcher, int maxResultCount)
    {
        Query q;

        // query for filename
        Query q1 = getQuery(queryStr, LocateCommons.COL_CONTENT);

        // query for path
        if ((queryStrPath != null) && (queryStrPath != "")) {
            Query q2 = getQueryPath(queryStrPath);

            BooleanQuery qb = new BooleanQuery();
            qb.Add(q1, Occur.MUST);
            qb.Add(q2, Occur.MUST);


            //Copied from:
            //    How do I combine queries with a boolean query? - Web Tutorials - avajava.com
            //    http://www.avajava.com/tutorials/lessons/how-do-i-combine-queries-with-a-boolean-query.html

            q = qb;
        } else {
            q = q1;
        }

        // resolve query
        TopScoreDocCollector collector = TopScoreDocCollector.Create(maxResultCount, true);
        searcher.Search(q, collector);
        ScoreDoc[] hits = collector.TopDocs().ScoreDocs;
        return hits;
    }

    public static Query getQueryPath(string pathStr)
    {
        BooleanQuery booleanQuery = new BooleanQuery();
        pathStr = TrimLastPathSeparator(pathStr);
        pathStr = LowerDriveChar(pathStr);
        Query query = new WildcardQuery(new Term(LocateCommons.COL_KEY, pathStr + "\\*"));
        booleanQuery.Add(query, Occur.SHOULD);
        return query;
    }

    public static string LowerDriveChar(string pathStr) {
        string result = pathStr;
        int drivePos = pathStr.IndexOf(':');
        if (drivePos > 0) {
            string prev = "";
            if (drivePos > 1) {
                prev = pathStr.Substring(0, drivePos-2);
            }
            string post = pathStr.Substring(drivePos);
            result = prev + Char.ToLowerInvariant(pathStr[drivePos-1]) + post;
        }
        
        return result;
    }

    public static string prepareForContent(string content)
    {
        string result = LocateCommons.LatinToAscii(content);
        result = result.ToLower();
        return result;
    }

    public static string TrimLastPathSeparator(string path)
    {
        char[] charsToTrimFromPath = {'\\', '/'};
        return path.TrimEnd(charsToTrimFromPath);
    }

    static Query getQuery(string querystr, string colName)
    {
        Analyzer analyzer = new KeywordAnalyzer();
        QueryParser parser = new QueryParser(Lucene.Net.Util.Version.LUCENE_30, colName, analyzer);
        if (querystr.StartsWith("*")) {
            parser.AllowLeadingWildcard = true;
        }
        Query q = parser.Parse(querystr);
        return q;
    }

    public static IndexSearcher getIndexSearcher(Lucene.Net.Store.Directory directory)
    {
        IndexSearcher searcher = new IndexSearcher(directory, true);
        return searcher;
    }

    public static string LatinToAscii(string InString)
    {
        string newString = string.Empty, charString;
        char ch;
        int charsCopied;

        for (int i = 0; i < InString.Length; i++) {
            charString = InString.Substring(i, 1);
            charString = charString.Normalize(NormalizationForm.FormKD);
            // If the character doesn't decompose, leave it as-is
            if (charString.Length == 1)
                newString += charString;
            else {
                charsCopied = 0;
                for (int j = 0; j < charString.Length; j++) {
                    ch = charString[j];
                    // If the char is 7-bit ASCII, add
                    if (ch < 128) {
                        newString += ch;
                        charsCopied++;
                    }
                }
                /* If we've decomposed non-ASCII, give it back
                 * in its entirety, since we only mean to decompose
                 * Latin chars.
                 */
                if (charsCopied == 0)
                    newString += InString.Substring(i, 1);
            }
        }
        return newString;
    }

    public static string getProtocol(string path)
    {
        string[] parts = path.Split(':');
        return parts[0].ToLower();
    }

    public static bool isProtocolPath(string path, string protocolPrefix)
    {
        string sPattern = @"^("+ protocolPrefix +@"):.*";
        return Regex.IsMatch(path, sPattern, RegexOptions.IgnoreCase);
    }

    /// <summary>
    /// Indicate file in archive.
    /// </summary>
    /// <param name="path">full path to file (start with path to archive)</param>
    /// <returns>true if file is in archive</returns>
    public static bool isInArchive(string path)
    {
        string sPattern = @"\.("+ ARCHIVE_SUFFIX +@")\\.*";
        return Regex.IsMatch(path, sPattern, RegexOptions.IgnoreCase);
    }

    /// <summary>
    /// Indicate archive for creating index.
    /// </summary>
    /// <param name="path">full path to file</param>
    /// <returns>true if file is archive for indexing</returns>
    public static bool gotoArchive(string path)
    {
        string sPatternEnd = @"\.("+ ARCHIVE_SUFFIX +@")$";
        return !isInArchive(path) && Regex.IsMatch(path, sPatternEnd, RegexOptions.IgnoreCase);
    }

    /// <summary>
    /// get path to archive.
    /// </summary>
    /// <param name="path">full path to file in archive</param>
    /// <returns>full path to archive</returns>
    public static string getArchivePath(string path)
    {
        string sPattern = @"^(.*\.("+ ARCHIVE_SUFFIX +@"))\\.*";
        Match match = Regex.Match(path, sPattern, RegexOptions.IgnoreCase);
        if (match.Success) {
            return match.Groups[1].Value;
        } else {
            return "";
        }
    }

    /// <summary>
    /// get path to file in archive
    /// </summary>
    /// <param name="path">full path to file in archive</param>
    /// <returns>full path in archive</returns>
    public static string getArchiveInternalPath(string path)
    {
        string sPattern = @"^(.*\.("+ ARCHIVE_SUFFIX +@"))\\(.*)";
        Match match = Regex.Match(path, sPattern, RegexOptions.IgnoreCase);
        if (match.Success) {
            return match.Groups[3].Value;
        } else {
            return "";
        }
    }

    /// <summary>
    /// get only fileName from path
    /// </summary>
    /// <param name="path">full path (may be in archive)</param>
    /// <returns>filename</returns>
    public static string getFileName(string path)
    {
        string sPattern = @"^.*\\(.*)";
        Match match = Regex.Match(path, sPattern, RegexOptions.IgnoreCase);
        if (match.Success) {
            return match.Groups[1].Value;
        } else {
            return "";
        }
    }

    public static bool isNetworkPath(string path)
    {
        string sPattern = @"(^\\\\|^http://|^https://|^ftp://)";
        return Regex.IsMatch(path, sPattern, RegexOptions.IgnoreCase);
    }
}
